package com.zhongcheng.superaudioplayer.WIPlayer;

import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.os.Environment;
import android.util.Log;

import com.zhongcheng.superaudioplayer.Listener.WIOnErrorListener;
import com.zhongcheng.superaudioplayer.Listener.WIOnLoadListener;
import com.zhongcheng.superaudioplayer.Listener.WIOnPauseListener;
import com.zhongcheng.superaudioplayer.Listener.WIOnResumListener;
import com.zhongcheng.superaudioplayer.Listener.WIOnTimeinfoListener;
import com.zhongcheng.superaudioplayer.Listener.WIOnpreparedListener;
import com.zhongcheng.superaudioplayer.WlTimeInfoBean;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

public class WIPlayer {

    private static final String TAG = "WIPlayer";
    public static final int RIGHT_CHANNEL = 0;
    public static final int LEFT_CHANNEL = 1;
    public static final int DOUBLE_CHANNEL = 2;

    static {
        System.loadLibrary("avutil-55");
        System.loadLibrary("swresample-2");
        System.loadLibrary("avcodec-57");
        System.loadLibrary("avformat-57");
        System.loadLibrary("swscale-4");
        System.loadLibrary("postproc-54");
        System.loadLibrary("avfilter-6");
        System.loadLibrary("avdevice-57");
        System.loadLibrary("soundTouch-lib");
        System.loadLibrary("native-lib");
    }

    private String mSource;
    private WIOnpreparedListener mPreparedListener;
    private WIOnLoadListener mOnLoadListener;
    private WIOnPauseListener mOnPauseListener;
    private WIOnResumListener mOnResumListener;
    private WIOnTimeinfoListener mOnTimeinfoListener;
    private WIOnErrorListener mOnErrorListner;
    private static WlTimeInfoBean wlTimeInfoBean;
    private static boolean initmediacodec = false;
    private int duration;

    private File pcmOutFile = new File(Environment.getExternalStorageDirectory().getAbsoluteFile(),"text.pcm");
    private FileOutputStream pcmfos ;

    public WIPlayer() {
    }

    public void setSource(String source) {
        this.mSource = source;
    }

    public void setPreparedListener(WIOnpreparedListener preparedListener) {
        this.mPreparedListener = preparedListener;
    }

    public void setmOnLoadListener(WIOnLoadListener onLoadListener) {
        this.mOnLoadListener = onLoadListener;
    }

    public void setmOnPauseListener(WIOnPauseListener onPauseListener) {
        this.mOnPauseListener = onPauseListener;
    }

    public void setmOnResumListener(WIOnResumListener onResumListener) {
        this.mOnResumListener = onResumListener;
    }

    public void setmOnTimeinfoListener(WIOnTimeinfoListener onTimeinfoListener) {
        this.mOnTimeinfoListener = onTimeinfoListener;
    }

    public void setmOnErrorListner(WIOnErrorListener onErrorListner) {
        this.mOnErrorListner = onErrorListner;
    }

    public void onPrepared() {
        if (mPreparedListener != null) {
            mPreparedListener.onPrePared();
        }
    }

    public void onError(int code, String msg) {
        stop();
        if (mOnErrorListner != null) {
            mOnErrorListner.onError(code, msg);
        }
    }

    public void prepare() {

        if (mSource == null || mSource.length() == 0) {
            Log.e(TAG, "source is empty");
            return;
        }

        new Thread(new Runnable() {
            @Override
            public void run() {
                n_preare(mSource);
            }
        }).start();
    }

    public void timeInfo(int total, int current) {
        if (mOnTimeinfoListener != null) {
            if (wlTimeInfoBean == null) {
                wlTimeInfoBean = new WlTimeInfoBean();
            }
            wlTimeInfoBean.setCurrentTime(current);
            wlTimeInfoBean.setTotalTime(total);
            mOnTimeinfoListener.onTimeinfo(wlTimeInfoBean);
        }
    }

    public void pause() {
        if (mOnPauseListener != null) {
            mOnPauseListener.onPause();
        }

        n_pause();
    }

    public void startRecord(File outfile)
    {
        if(!initmediacodec)
        {
            if(n_samplerate() > 0)
            {
                initmediacodec = true;
                initAACEncoder(n_samplerate(), outfile);
                n_startstoprecord(true);
                Log.d(TAG,"开始录制");
            }
        }
    }

    public void stopRecord()
    {
        if(initmediacodec)
        {
            n_startstoprecord(false);
            releaseMedicacodec();
            Log.d(TAG,"完成录制");
        }
    }

    public void pauseRecord()
    {
        n_startstoprecord(false);
        Log.d(TAG,"暂停录制");
    }

    public void resumeRcord()
    {
        n_startstoprecord(true);
        Log.d(TAG,"继续录制");
    }

    public void onLoad(boolean load) {
        if (mOnLoadListener != null) {
            mOnLoadListener.onLoad(load);
        }
    }

    public void onResum() {
        if (mOnResumListener != null) {
            mOnResumListener.onResume();
        }

        n_resum();
    }

    public void start() {

        if (mSource == null || mSource.length() == 0) {
            Log.e(TAG, "source is empty");
            return;
        }

        new Thread(new Runnable() {
            @Override
            public void run() {
                n_start();
            }
        }).start();
    }

    public void stop() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                n_stop();
            }
        }).start();
    }

    public int getDuration() {
        if (duration == 0) {
            duration = n_duration();
        }
        return duration;
    }

    public void setVolume(int level) {
        if (level > 0) {
            n_volume(level);
        }
    }

    public void setPitch(double pitch) {
        n_setPitch(pitch);
    }

    public void setTempo(double tempo) {
        n_settempo(tempo);
    }

    public void setMute(int mute) {
        n_setMute(mute);
    }

    public void seek(int sceds) {
        n_seek(sceds);
    }

    private native void n_preare(String source);

    private native void n_start();

    private native void n_pause();

    private native void n_resum();

    private native void n_stop();

    private native void n_seek(int sceds);

    private native int n_duration();

    private native void n_volume(int vulume);

    private native void n_setMute(int mute);

    private native void n_setPitch(double pitch);

    private native void n_settempo(double tempo);

    private native void n_startstoprecord(boolean start);

    private native int n_samplerate();

    //MediaCodec

    private MediaCodec aacEncoder;
    private MediaFormat encoderFormat;
    private MediaCodec.BufferInfo info;
    private FileOutputStream outputStream = null;
    private int aacsamplerate = 4;
    private ByteBuffer[] encodeInputBuffers;
    private ByteBuffer[] encodeOutPutBuffers;
    private byte[] outBuffer;
    private int pcmSize;


    private void initAACEncoder(int samprate, File outFile) {
        try {
            aacsamplerate = getADTSsamplerate(samprate);
            encoderFormat = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, samprate, 2);
            encoderFormat.setInteger(MediaFormat.KEY_BIT_RATE, 96000);
            encoderFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectERLC);
            encoderFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 5000);
            aacEncoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);
            info = new MediaCodec.BufferInfo();
            if (aacEncoder == null) {
                Log.d(TAG, "craete encoder wrong");
                return;
            }
            pcmfos = new FileOutputStream(pcmOutFile);
            outputStream = new FileOutputStream(outFile);
            aacEncoder.configure(encoderFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
            aacEncoder.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void encodePCM2AAC(int size, byte[] buffer) {
        if (size > 0 && aacEncoder != null) {

            try {
                pcmfos.write(buffer,0,size);
            } catch (IOException e) {
                e.printStackTrace();
            }

            encodeInputBuffers = aacEncoder.getInputBuffers();
            int inputIndex = aacEncoder.dequeueInputBuffer(100);
            if (inputIndex >= 0) {
                ByteBuffer input = encodeInputBuffers[inputIndex];
                input.clear();
                input.put(buffer);
                aacEncoder.queueInputBuffer(inputIndex, 0, size, 0, 0);
            }
            encodeOutPutBuffers = aacEncoder.getOutputBuffers();
            int outputIndex = aacEncoder.dequeueOutputBuffer(info, 100);
            while (outputIndex >= 0) {
                try {
                    ByteBuffer output = encodeOutPutBuffers[outputIndex];
                    pcmSize = info.size + 7;
                    outBuffer = new byte[pcmSize];
                    output.position(info.offset);
                    output.limit(info.offset + info.size);
                    addADtsHeader(outBuffer, pcmSize, aacsamplerate);
                    output.get(outBuffer, 7, info.size);
                    output.position(info.offset);
                    outputStream.write(outBuffer, 0, pcmSize);
                    aacEncoder.releaseOutputBuffer(outputIndex,false);
                    outputIndex = aacEncoder.dequeueOutputBuffer(info, 100);
                    output = null;
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        }
    }

    private void addADtsHeader(byte[] packet, int packetLen, int samplerate) {
        int profile = 2; // AAC LC
        int freqIdx = samplerate; // samplerate
        int chanCfg = 2; // CPE

        packet[0] = (byte) 0xFF; // 0xFFF(12bit) 这里只取了8位，所以还差4位放到下一个里面
        packet[1] = (byte) 0xF9; // 第一个t位放F
        packet[2] = (byte) (((profile - 1) << 6) + (freqIdx << 2) + (chanCfg >> 2));
        packet[3] = (byte) (((chanCfg & 3) << 6) + (packetLen >> 11));
        packet[4] = (byte) ((packetLen & 0x7FF) >> 3);
        packet[5] = (byte) (((packetLen & 7) << 5) + 0x1F);
        packet[6] = (byte) 0xFC;
    }

    private int getADTSsamplerate(int samplerate) {
        int rate = 4;
        switch (samplerate) {
            case 96000:
                rate = 0;
                break;
            case 88200:
                rate = 1;
                break;
            case 64000:
                rate = 2;
                break;
            case 48000:
                rate = 3;
                break;
            case 44100:
                rate = 4;
                break;
            case 32000:
                rate = 5;
                break;
            case 24000:
                rate = 6;
                break;
            case 22050:
                rate = 7;
                break;
            case 16000:
                rate = 8;
                break;
            case 12000:
                rate = 9;
                break;
            case 11025:
                rate = 10;
                break;
            case 8000:
                rate = 11;
                break;
            case 7350:
                rate = 12;
                break;
        }
        return rate;
    }

    private void releaseMedicacodec()
    {
        if(aacEncoder == null)
        {
            return;
        }
        try {
            outputStream.close();
            outputStream = null;
            aacEncoder.stop();
            aacEncoder.release();
            aacEncoder = null;
            encoderFormat = null;
            info = null;
            initmediacodec = false;

            Log.d(TAG,"录制完成...");
        } catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            if(outputStream != null)
            {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                outputStream = null;
            }
        }
    }
}
